#include "Thread.hpp"
#include "Log.hpp"
#include "Task.hpp"
#include <vector>
#include <queue>

using std::cout;
using std::endl;
using std::queue;
using std::vector;
using namespace ThreadModule;

static const int g_default_threadnum = 5;

template <typename T>
class ThreadPool
{
public:
    static ThreadPool *CreateThreadPool(int threadnum = g_default_threadnum)
    {
        if(s_threadpool == nullptr)
        {
            pthread_mutex_lock(&s_poolmutex);
            if(s_threadpool == nullptr)
            {
                s_threadpool = new ThreadPool<T>(threadnum);
            }
            pthread_mutex_unlock(&s_poolmutex);
        }
        return s_threadpool;
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);

        for (auto &th : _threads)
        {
            delete th;
        }
    }

    void start()
    {
        _is_running = true;
        while (true)
        {
            if (_wait_threadnum > 0)
            {
                for (auto &th : _threads)
                {
                    if (th->isIdle()) // skip while thread is busy on other task
                    {
                        _threadWakeUp();
                        if (!_task_queue.empty())
                        {
                            cout << "<" << th->getMessage() << "> pid = " << th->native_handle() << endl;
                            _wait_threadnum--;

                            auto task_func = _task_queue.front();
                            th->start(*task_func);
                            _task_queue.pop();
                            delete task_func;

                            _wait_threadnum++;
                        }
                        else
                        {
                            cout << "task queue is empty ! waitting task push now..." << endl;
                            //_threadSleep();
                        }
                    }
                    else
                    {
                        cout << "thread is busying !  contribute the task to other thread..." << endl;
                    }
                }
                sleep(1);
            }
            else
            {
                cout << "All thread is busying !!!  just wait" << endl;
                _threadSleep();
            }
        }
    }

    void stop()
    {
        _is_running = false;
    }

    bool enQueue(const T &task)
    {
        bool ret = false;
        _lockQueue();
        if (_is_running)
        {
            // push the variable which type is std::function
            Task t;
            auto task_func = new std::function<void()>(std::bind(&Task::doing, &t));
            _task_queue.push(task_func);

            _threadWakeUp();

            ret = true;
        }
        _unlockQueue();
        return ret;
    }

private:
    ThreadPool(int threadnum = g_default_threadnum) : _threadnum(threadnum)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        _threads.reserve(threadnum);
        // just create Thread, but had not set task, thread is not running
        for (int i = 0; i < threadnum; i++)
        {
            _threads.emplace_back(new Thread("thread-" + std::to_string(i + 1)));
        }
    }

private:
    void _threadSleep()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void _threadWakeUp()
    {
        pthread_cond_signal(&_cond);
    }
    void _threadWakeUpAll()
    {
        pthread_cond_broadcast(&_cond);
    }
    void _lockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void _unlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }

private:
    int _threadnum;
    vector<Thread *> _threads;
    queue<std::function<void()> *> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    bool _is_running = false;
    int _wait_threadnum = _threadnum;

    static ThreadPool<T>* s_threadpool;
    static pthread_mutex_t s_poolmutex;
};
template <typename T>
ThreadPool<T>* ThreadPool<T>::s_threadpool = nullptr;
template <typename T>
pthread_mutex_t ThreadPool<T>::s_poolmutex = PTHREAD_MUTEX_INITIALIZER;